{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 使用兼容 NetworkX 的 API 进行图操作\n",
    "\n",
    "GraphScope 支持使用兼容 NetworkX 的 API 进行图操作。\n",
    "本次教程参考了 [tutorial in NetworkX](https://networkx.org/documentation/stable/tutorial.html) 的组织方式来介绍这些 API。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Install graphscope package if you are NOT in the Playground\n",
    "\n",
    "!pip3 install graphscope"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Import the graphscope and graphscope networkx module.\n",
    "\n",
    "import graphscope\n",
    "import graphscope.nx as nx\n",
    "\n",
    "graphscope.set_option(show_log=False)  # enable logging"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 创建图\n",
    "\n",
    "创建一个空图，只需要简单地创建一个 Graph 对象。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "G = nx.Graph()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 点\n",
    "\n",
    "图 `G` 可以通过多种方式进行扩充。  在 graphscope.nx 中， 支持一些可哈希的 Python object 作为图的点, 其中包括 int，str，float，tuple，bool 对象。\n",
    "首先，我们从空图和简单的图操作开始，如下所示，你可以一次增加一个顶点，"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "G.add_node(1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "也可以从任何[可迭代](https://docs.python.org/3/glossary.html#term-iterable)的容器中增加顶点，例如一个列表"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "G.add_nodes_from([2, 3])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "你也可以通过格式为`(node, node_attribute_dict)`的二元组的容器， 将点的属性和点一起添加，如下所示：\n",
    "\n",
    "点属性我们将在后面进行讨论。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "G.add_nodes_from(\n",
    "    [\n",
    "        (4, {\"color\": \"red\"}),\n",
    "        (5, {\"color\": \"green\"}),\n",
    "    ]\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "一个图的节点也可以直接添加到另一个图中："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "H = nx.path_graph(10)\n",
    "G.add_nodes_from(H)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "经过上面的操作后，现在图 `G` 中包含了图 `H` 的节点。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "list(G.nodes)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "list(G.nodes.data())  # shows the node attributes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 边\n",
    "\n",
    "图 `G` 也可以一次增加一条边来进行扩充，"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "G.add_edge(1, 2)\n",
    "e = (2, 3)\n",
    "G.add_edge(*e)  # unpack edge tuple*"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "list(G.edges)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "或者通过一次增加包含多条边的list，"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "G.add_edges_from([(1, 2), (1, 3)])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "list(G.edges)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "或者通过增加任意 `ebunch` 的边。 *ebunch* 表示任意一个可迭代的边元组的容器。一个边元组可以是一个只包含首尾两个顶点的二元组，例如 `(1, 3)` ，或者一个包含顶点和边属性字典的三元组，例如 `(2, 3, {'weight': 3.1415})`。\n",
    "\n",
    "边属性我们会在后面进行讨论"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "G.add_edges_from([(2, 3, {\"weight\": 3.1415})])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "list(G.edges.data())  # shows the edge arrtibutes"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "G.add_edges_from(H.edges)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "list(G.edges)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "用户也可以通过 `.update(nodes, edges)` 同时增加点和边"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "G.update(edges=[(10, 11), (11, 12)], nodes=[10, 11, 12])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "list(G.nodes)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "list(G.edges)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "当增加已存在的点或边时，这些点和边会被忽略，不会产生报错。如下所示，在去除掉所有的点和边之后，"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "G.clear()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "这里我们增加点和边，graphscope.nx 会忽略掉已经存在的点和边。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "G.add_edges_from([(1, 2), (1, 3)])\n",
    "G.add_node(1)\n",
    "G.add_edge(1, 2)\n",
    "G.add_node(\"spam\")  # adds node \"spam\"\n",
    "G.add_nodes_from(\"spam\")  # adds 4 nodes: 's', 'p', 'a', 'm'\n",
    "G.add_edge(3, \"m\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "目前图 `G` 共包含8个顶点和3条边，可以使用如下所示方法进行查看："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "G.number_of_nodes()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "G.number_of_edges()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "## 查看图的元素\n",
    "\n",
    "我们可以查看图的顶点和边。可以使用四种基本的图属性来查看图元素：`G.nodes`，`G.edges`，`G.adj` 和 `G.degree`。这些属性都是 `set-like` 的视图，分别表示图中点，边，点邻居和度数。这些接口提供了一个只读的关于图结构的视图。这些视图也可以像字典一样，用户可以查看点和边的属性，然后通过方法 `.items()`，`.data('span')` 遍历数据属性。\n",
    "\n",
    "用户可以指定使用一个特定的容器类型，而不是一个视图。这里我们使用了lists，然而sets, dicts, tuples和其他容器可能在其他情况下更合适。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "list(G.nodes)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "list(G.edges)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "list(G.adj[1])  # or list(G.neighbors(1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "G.degree[1]  # the number of edges incident to 1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "用户可以使用一个 *nbunch* 来查看一个点子集的边和度。一个 *nbunch* 可以是 `None` （表示全部节点），一个节点或者一个可迭代的顶点容器。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "G.edges([2, \"m\"])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "G.degree([2, 3])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "## 删除图元素\n",
    "\n",
    "用户可以使用类似增加节点和边的方式来从图中删除顶点和边。\n",
    "相关方法\n",
    "`Graph.remove_node()`,\n",
    "`Graph.remove_nodes_from()`,\n",
    "`Graph.remove_edge()`\n",
    "和\n",
    "`Graph.remove_edges_from()`, 例如"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "G.remove_node(2)\n",
    "G.remove_nodes_from(\"spam\")\n",
    "list(G.nodes)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "list(G.edges)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "G.remove_edge(1, 3)\n",
    "G.remove_edges_from([(1, 2), (2, 3)])\n",
    "list(G.edges)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "## 使用图构造函数来构建图\n",
    "\n",
    "图对象并不一定需要以增量的方式构建 - 用户可以直接将图数据传给 Graph/DiGraph 的构造函数来构建图对象。\n",
    "当通过实例化一个图类来创建一个图结构时，用户可以使用多种格式来指定图数据， 如下所示。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "G.add_edge(1, 2)\n",
    "H = nx.DiGraph(G)  # create a DiGraph using the connections from G\n",
    "list(H.edges())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "edgelist = [(0, 1), (1, 2), (2, 3)]\n",
    "H = nx.Graph(edgelist)\n",
    "list(H.edges)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "## 访问边和邻居\n",
    "\n",
    "除了通过 `Graph.edges` 和 `Graph.adj` 视图外，用户也可以通过下标来访问边和顶点的邻居；"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "G = nx.Graph([(1, 2, {\"color\": \"yellow\"})])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "G[1]  # same as G.adj[1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "G[1][2]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "G.edges[1, 2]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "当边已经存在时，可以通过下标来获取或设置边的属性："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "G.add_edge(1, 3)\n",
    "G[1][3][\"color\"] = \"blue\"\n",
    "G.edges[1, 3]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "G.edges[1, 2][\"color\"] = \"red\"\n",
    "G.edges[1, 2]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "用户可以通过 `G.adjacency()`, 或 `G.adj.items()` 快速地查看所有点的 `(节点，邻居）`对。如下所示：\n",
    "\n",
    "注意当图是无向图时，每条边会在遍历时出现两次。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "FG = nx.Graph()\n",
    "FG.add_weighted_edges_from([(1, 2, 0.125), (1, 3, 0.75), (2, 4, 1.2), (3, 4, 0.375)])\n",
    "for n, nbrs in FG.adj.items():\n",
    "    for nbr, eattr in nbrs.items():\n",
    "        wt = eattr[\"weight\"]\n",
    "        if wt < 0.5:\n",
    "            print(f\"({n}, {nbr}, {wt:.3})\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如下所示，可以方便地访问所有边和边的属性。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "for (u, v, wt) in FG.edges.data(\"weight\"):\n",
    "    if wt < 0.5:\n",
    "        print(f\"({u}, {v}, {wt:.3})\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "## 添加图属性，顶点属性和边属性\n",
    "\n",
    "属性如权重、标签、颜色等可以被attach到图、点或者边上。\n",
    "\n",
    "每个图、节点和边都可以保存 key/value 属性，默认属性是空。属性可以通过 `add_edge`, `add_node` 或直接对属性字典进行操作来增加或修改属性。\n",
    "\n",
    "### 图属性\n",
    "\n",
    "在创建新图的时候定义图属性"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "G = nx.Graph(day=\"Friday\")\n",
    "G.graph"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "或者在创建后修改图属性"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "G.graph[\"day\"] = \"Monday\"\n",
    "G.graph"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "### 节点属性\n",
    "\n",
    "可以使用 `add_node()`, `add_nodes_from()`, or `G.nodes` 等方法增加节点属性。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "G.add_node(1, time=\"5pm\")\n",
    "G.add_nodes_from([3], time=\"2pm\")\n",
    "G.nodes[1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "G.nodes[1][\"room\"] = 714\n",
    "G.nodes.data()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "注意 向 `G.nodes` 增加一个节点并不会真正增加节点到图中，如果需要增加新节点，应该使用 `G.add_node()`. 边的使用同样如此。\n",
    "\n",
    "\n",
    "### 边属性\n",
    "\n",
    "\n",
    "可以通过 `add_edge()`, `add_edges_from()` 或下标来增加或修改边属性。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "G.add_edge(1, 2, weight=4.7)\n",
    "G.add_edges_from([(3, 4), (4, 5)], color=\"red\")\n",
    "G.add_edges_from([(1, 2, {\"color\": \"blue\"}), (2, 3, {\"weight\": 8})])\n",
    "G[1][2][\"weight\"] = 4.7\n",
    "G.edges[3, 4][\"weight\"] = 4.2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "G.edges.data()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "特殊的属性如 `weight` 的值应该是数值型，因为一些需要带权重的边的算法会使用到这一属性。\n",
    "\n",
    "\n",
    "## 抽取子图和边子图\n",
    "\n",
    "graphscope.nx 支持通过传入一个点集或边集来抽取一个 `deepcopy` 的子图。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "G = nx.path_graph(10)\n",
    "# induce a subgraph by nodes\n",
    "H = G.subgraph([0, 1, 2])\n",
    "list(H.nodes)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "list(H.edges)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# induce a edge subgraph by edges\n",
    "K = G.edge_subgraph([(1, 2), (3, 4)])\n",
    "list(K.nodes)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "list(K.edges)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "需要注意的是，这里抽取子图与NetworkX的实现有一些区别，NetworkX返回的是一个子图的视图，但 graphscope.nx 返回的子图是一个独立于原始图的子图或边子图。\n",
    "\n",
    "## 图的拷贝\n",
    "\n",
    "用户可以使用 `to_directed` 方法来获取一个图的有向表示。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "DG = G.to_directed()  # here would return a \"deepcopy\" directed representation of G.\n",
    "list(DG.edges)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# or with\n",
    "DGv = G.to_directed(as_view=True)  # return a view.\n",
    "list(DGv.edges)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# or with\n",
    "DG = nx.DiGraph(G)  # return a \"deepcopy\" of directed representation of G.\n",
    "list(DG.edges)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "或者通过 `copy` 方法得到一个图的拷贝。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "H = G.copy()  # return a view of copy\n",
    "list(H.edges)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# or with\n",
    "H = G.copy(as_view=False)  # return a \"deepcopy\" copy\n",
    "list(H.edges)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# or with\n",
    "H = nx.Graph(G)  # return a \"deepcopy\" copy\n",
    "list(H.edges)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "注意，graphscope.nx 不支持浅拷贝。\n",
    "\n",
    "\n",
    "## 有向图\n",
    "\n",
    "`DiGraph` 类提供了额外的方法和属性来指定有向边，如：`DiGraph.out_edges`, `DiGraph.in_degree`,\n",
    "`DiGraph.predecessors()`, `DiGraph.successors()` etc.\n",
    "\n",
    "为了让算法方便地在两种图类型上运行，有向版本的 `neighbors` 等同于 `successors()` ，`degree` 返回 `in_degree` 和 `out_degree` 的和。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "DG = nx.DiGraph()\n",
    "DG.add_weighted_edges_from([(1, 2, 0.5), (3, 1, 0.75)])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "DG.out_degree(1, weight=\"weight\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "DG.degree(1, weight=\"weight\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "list(DG.successors(1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "list(DG.neighbors(1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "list(DG.predecessors(1))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "在 graphscope.nx 中，存在有些算法仅能用于有向图的分析，而另一些算法仅能用于无向图的分析。如果你想将一个有向图转化为无向图，你可以使用 `Graph.to_undirected()`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "H = DG.to_undirected()  # return a \"deepcopy\" of undirected represetation of DG.\n",
    "list(H.edges)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# or with\n",
    "H = nx.Graph(DG)  # create an undirected graph H from a directed graph G\n",
    "list(H.edges)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "DiGraph 也可以通过 `DiGraph.reverse()` 来反转边。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "K = DG.reverse()  # retrun a \"deepcopy\" of reversed copy.\n",
    "list(K.edges)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# or with\n",
    "K = DG.reverse(copy=False)  # return a view of reversed copy.\n",
    "list(K.edges)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "## 图分析\n",
    "图 `G` 的结构可以通过使用各式各样的图理论函数进行分析，例如："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "G = nx.Graph()\n",
    "G.add_edges_from([(1, 2), (1, 3)])\n",
    "G.add_node(4)\n",
    "sorted(d for n, d in G.degree())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "nx.builtin.clustering(G)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "在 graphscope.nx 中，我们支持用于图分析的内置算法，算法的详细内容可以参考 [builtin algorithm](https://graphscope.io/docs/reference/networkx/builtin.html)\n",
    "\n",
    "\n",
    "## 通过 GraphScope graph object来创建图\n",
    "\n",
    "除了通过networkx的方式创建图之外，我们也可以使用标准的 `GraphScope` 的方式创建图，这一部分将会在下一个教程中进行介绍，下面我们展示一个简单的示例："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# we load a GraphScope graph with load_ldbc\n",
    "from graphscope.dataset import load_ldbc\n",
    "\n",
    "graph = load_ldbc(directed=False)\n",
    "\n",
    "# create graph with the GraphScope graph object\n",
    "G = nx.Graph(graph)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "## 将图转化为graphscope.graph\n",
    "\n",
    "\n",
    "正如同 graphscope.nx Graph 可以从 GraphScope graph 转化而来，graphscope.nx Graph也可以转化为 GraphScope graph. 例如："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "nodes = [(0, {\"foo\": 0}), (1, {\"foo\": 1}), (2, {\"foo\": 2})]\n",
    "edges = [(0, 1, {\"weight\": 0}), (0, 2, {\"weight\": 1}), (1, 2, {\"weight\": 2})]\n",
    "G = nx.Graph()\n",
    "G.update(edges, nodes)\n",
    "g = graphscope.g(G)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
